﻿//
//  Photo Slider
//  Copyright (C) 2011 Victor Tereschenko (aka sibvic)
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//     
// ========================================================================

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;

namespace PhotoSlider
{
    class ImagesSource : IDisposable
    {
        public ImagesSource(ImagesLoader imagesLoader, int width, int height)
        {
            for (int i = 0; i < 4; ++i)
            {
                var data = new Data();
                data.mWidth = width;
                data.mHeight = height;
                data.mImagesLoader = imagesLoader;
                mData.Add(data);

                Thread preloader = new Thread(new ParameterizedThreadStart(preloaderWork));
                preloader.Start(data);
                mPreloader.Add(preloader);
            }
        }
                
        public void SetSize(int width, int height)
        {
            foreach (Data data in mData)
            {
                lock (data.mImagesLoader)
                {
                    data.mWidth = width;
                    data.mHeight = height;
                }
                data.mImagesLoadedEvent.Set();
            }
        }

        void preloaderWork(object queue)
        {
            Data data = (Data)queue;
            var proc = new Microsoft.VisualBasic.Devices.ComputerInfo();
            bool haveWork = true;
            while (haveWork)
            {
                int width = 0;
                int height = 0;
                do
                {
                    lock (data)
                    {
                        width = data.mWidth;
                        height = data.mHeight;
                    }
                    StackableImage image = data.mImagesLoader.Next();
                    if (image == null)
                    {
                        haveWork = false;
                        break;
                    }
                    if (image.Get(width, height) == null)
                    {
                        image.Dispose();
                        continue;
                    }
                    data.AddImage(image);
                }
                while (proc.AvailablePhysicalMemory > 1024 * 1024 * 100);
                data.mImagesLoadedEvent.Reset();
                data.mImagesLoadedEvent.WaitOne();
            }
            data.IsDone = true;
        }

        class Data
        {
            public ImagesLoader mImagesLoader;
            public ManualResetEvent mImagesLoadedEvent = new ManualResetEvent(true);
            public int mWidth;
            public int mHeight;
            public bool IsDone = false;

            object mLoadedQueueLock = new object();
            Queue<StackableImage> mPreloaded = new Queue<StackableImage>();
            
            public void AddImage(StackableImage image)
            {
                lock (mLoadedQueueLock)
                {
                    mPreloaded.Enqueue(image);
                }
            }

            public Queue<StackableImage> GetImages()
            {
                lock (mLoadedQueueLock)
                {
                    var queue = mPreloaded;
                    mPreloaded = new Queue<StackableImage>();
                    return queue;
                }
            }
        }

        List<Thread> mPreloader = new List<Thread>();
        List<Data> mData = new List<Data>();
        Queue<StackableImage> mImagesCache = new Queue<StackableImage>();

        public void PushBack(StackableImage img)
        {
            mImagesCache.Enqueue(img);
        }

        public StackableImage GetNext()
        {
            while (true)
            {
                if (mImagesCache.Count > 0)
                    return mImagesCache.Dequeue();

                bool haveWorkingLoaders = false;
                foreach (Data data in mData)
                {
                    if (data.IsDone)
                        continue;
                    var images = data.GetImages();
                    while (images.Count > 0)
                        mImagesCache.Enqueue(images.Dequeue());
                    data.mImagesLoadedEvent.Set();
                    haveWorkingLoaders = true;
                }
                if (mImagesCache.Count > 0)
                    return mImagesCache.Dequeue();
                if (haveWorkingLoaders)
                {
                    Thread.Sleep(100);
                    continue;
                }
                return null;
            }
        }

        bool mDisposing = false;
        public void Dispose()
        {
            if (mDisposing)
                return;
            mDisposing = true;
            foreach (Thread thread in mPreloader)
                thread.Abort();
        }
    }
}
